#include <stdio.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

#include "memcached.h"

static char *uriencode_map[256];
static char uriencode_str[768];

void uriencode_init(void) {
    int x;
    char *str = uriencode_str;
    for (x = 0; x < 256; x++) {
        if (isalnum(x) || x == '-' || x == '.' || x == '_' || x == '~') {
            uriencode_map[x] = NULL;
        } else {
            snprintf(str, 4, "%%%02hhX", (unsigned char)x);
            uriencode_map[x] = str;
            str += 3; /* lobbing off the \0 is fine */
        }
    }
}

bool uriencode(const char *src, char *dst, const size_t srclen, const size_t dstlen) {
    int x;
    size_t d = 0;
    for (x = 0; x < srclen; x++) {
        if (d + 4 >= dstlen)
            return false;
        if (uriencode_map[(unsigned char) src[x]] != NULL) {
            memcpy(&dst[d], uriencode_map[(unsigned char) src[x]], 3);
            d += 3;
        } else {
            dst[d] = src[x];
            d++;
        }
    }
    dst[d] = '\0';
    return true;
}

/* Avoid warnings on solaris, where isspace() is an index into an array, and gcc uses signed chars */
#define xisspace(c) isspace((unsigned char)c)

bool safe_strtoull(const char *str, uint64_t *out) {
    assert(out != NULL);
    errno = 0;
    *out = 0;
    char *endptr;
    unsigned long long ull = strtoull(str, &endptr, 10);
    if ((errno == ERANGE) || (str == endptr)) {
        return false;
    }

    if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
        if ((long long) ull < 0) {
            /* only check for negative signs in the uncommon case when
             * the unsigned number is so big that it's negative as a
             * signed number. */
            if (strchr(str, '-') != NULL) {
                return false;
            }
        }
        *out = ull;
        return true;
    }
    return false;
}

bool safe_strtoll(const char *str, int64_t *out) {
    assert(out != NULL);
    errno = 0;
    *out = 0;
    char *endptr;
    long long ll = strtoll(str, &endptr, 10);
    if ((errno == ERANGE) || (str == endptr)) {
        return false;
    }

    if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
        *out = ll;
        return true;
    }
    return false;
}

bool safe_strtoul(const char *str, uint32_t *out) {
    char *endptr = NULL;
    unsigned long l = 0;
    assert(out);
    assert(str);
    *out = 0;
    errno = 0;

    l = strtoul(str, &endptr, 10);
    if ((errno == ERANGE) || (str == endptr)) {
        return false;
    }

    if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
        if ((long) l < 0) {
            /* only check for negative signs in the uncommon case when
             * the unsigned number is so big that it's negative as a
             * signed number. */
            if (strchr(str, '-') != NULL) {
                return false;
            }
        }
        *out = l;
        return true;
    }

    return false;
}

bool safe_strtol(const char *str, int32_t *out) {
    assert(out != NULL);
    errno = 0;
    *out = 0;
    char *endptr;
    long l = strtol(str, &endptr, 10);
    if ((errno == ERANGE) || (str == endptr)) {
        return false;
    }

    if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
        *out = l;
        return true;
    }
    return false;
}

bool safe_strtod(const char *str, double *out) {
    assert(out != NULL);
    errno = 0;
    *out = 0;
    char *endptr;
    double d = strtod(str, &endptr);
    if ((errno == ERANGE) || (str == endptr)) {
        return false;
    }

    if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
        *out = d;
        return true;
    }
    return false;
}

void vperror(const char *fmt, ...) {
    int old_errno = errno;
    char buf[1024];
    va_list ap;

    va_start(ap, fmt);
    if (vsnprintf(buf, sizeof(buf), fmt, ap) == -1) {
        buf[sizeof(buf) - 1] = '\0';
    }
    va_end(ap);

    errno = old_errno;

    perror(buf);
}

/** add by steven@2015-03-25 */
struct acl_node ACL_NODES[10000];
int ACL_NODES_TAIL = 0;

/*
static void disploy_acl_nodes() {
    int i;
    for (i = 0; i < ACL_NODES_TAIL; i++) {
        int k;
        for (k = 31; k >= 0; k--) {
            printf("%c", ((0x1 << k) & ACL_NODES[i].net) ? '1' : '0');
            if (k % 8 == 0)  printf(" ");
        }
        printf(" mask_lenth = %2d \n", ACL_NODES[i].len);
    }
}
*/

static int parse_mask(char *mask) {

    int imask = atoi(mask);
    int rtn = 0;
    int i;
    
    for (i = 31; i >= (32 - imask); i--) {
        rtn += (0x1 << i);
    }
    
    return rtn;
}

static struct acl_node parse_acl_node(char *line) {

    char *addr = strtok(line, "/");
    char *mask = strtok(NULL, "/");
    if (NULL == mask) {
        mask = "32";
    }

    int net = 0;
    char *p = strtok(addr, ".");
    while (NULL != p) {
        net = (net << 8) + atoi(p);
        p = strtok(NULL, ".");
    }
    
    net = net & parse_mask(mask);
    
    struct acl_node netNode;
    netNode.net = net;
    netNode.len = atoi(mask);

    return netNode;
} 

static int parse_address(char *ip) {

    char addr[100];
    int rtn = 0;

    strcpy(addr, ip);
    char *result = strtok(addr, ".");
    while (NULL != result) {
        int i = atoi(result);
        rtn = (rtn << 8) + i;
        result = strtok(NULL, ".");
    }
    return rtn;
}

bool acl_check(char *ip) {
    int rtn = 0;
    int addr = parse_address(ip);
    
    int i;
    for (i = 0; i < ACL_NODES_TAIL; i++) {
        int k;
        int len = 32 - ACL_NODES[i].len;
        for (k = 31; k >= len; k--) {
            if (((0x1 << k) & ACL_NODES[i].net) != ((0x1 << k) & addr)) {
                break;
            }
        }
        
        if (k == (len - 1)) {
            return 1;
        }
    }

    return rtn;
}

void init_acl_list(char *acl_file) {

    printf("access control file: %s\n", acl_file);

    FILE *fp;
    char line[1025];

    if ( NULL == (fp = fopen(acl_file, "r")) ) {
        printf("fopen error!\n");
        exit(EXIT_FAILURE);
    }

    while (fgets(line, sizeof line, fp)) {
        struct acl_node node = parse_acl_node(line);
        ACL_NODES[ACL_NODES_TAIL++] = node;
    }

    fclose(fp);
}
/** add by steven@2015-03-25 */

#ifndef HAVE_HTONLL
static uint64_t mc_swap64(uint64_t in) {
#ifdef ENDIAN_LITTLE
    /* Little endian, flip the bytes around until someone makes a faster/better
    * way to do this. */
    int64_t rv = 0;
    int i = 0;
     for(i = 0; i<8; i++) {
        rv = (rv << 8) | (in & 0xff);
        in >>= 8;
     }
    return rv;
#else
    /* big-endian machines don't need byte swapping */
    return in;
#endif
}

uint64_t ntohll(uint64_t val) {
   return mc_swap64(val);
}

uint64_t htonll(uint64_t val) {
   return mc_swap64(val);
}
#endif

